using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;


namespace MediaDC
{
    static class NativeConsole
    {
        private static bool hasConsole = false;
        private static IntPtr conOut;
        private static IntPtr oldOut;

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = false)]
        private static extern bool FreeConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CreateFile(
            string fileName,
            int desiredAccess,
            int shareMode,
            IntPtr securityAttributes,
            int creationDisposition,
            int flagsAndAttributes,
            IntPtr templateFile);

        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);

        public static void CreateConsole()
        {
            if (hasConsole)
                return;

            if (oldOut == IntPtr.Zero)
                oldOut = GetStdHandle(-11);

            if (!AllocConsole())
                throw new Exception("AllocConsole() failed");

            conOut = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);

            if (!SetStdHandle(-11, conOut))
                throw new Exception("SetStdHandle() failed");

            StreamToConsole();

            hasConsole = true;
        }

        private static void StreamToConsole()
        {
            Stream cstm = Console.OpenStandardOutput();
            StreamWriter cstw = new StreamWriter(cstm);
            cstw.AutoFlush = true;
            Console.SetOut(cstw);
            Console.SetError(cstw);
        }

        public static void ReleaseConsole()
        {
            if (!hasConsole)
                return;

            if (!CloseHandle(conOut))
                throw new Exception("CloseHandle() failed");

            conOut = IntPtr.Zero;

            if (!FreeConsole())
                throw new Exception("FreeConsole() failed");

            if (!SetStdHandle(-11, oldOut))
                throw new Exception("SetStdHandle() failed");

            StreamToConsole();

            hasConsole = false;
        }
    }


    [StructLayout(LayoutKind.Sequential)]
    struct SHFILEINFO
    {
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }

    static class NativeImageList
    {
        private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
        private const uint SHGFI_SMALLICON = 0x1; // 'Small icon
        private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
        public static uint SHGFI_SHELLICONSIZE = 0x4;
        public static uint SHGFI_SYSICONINDEX = 0x4000;

        private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
        private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

        public static uint LVM_FIRST = 0x1000;
        public static uint LVM_SETIMAGELIST = LVM_FIRST + 3;

        public static uint LVSIL_NORMAL = 0;
        public static uint LVSIL_SMALL = 1;

        private static readonly int _directoryIconIndex;
        private static readonly IntPtr _largeImageList;
        private static readonly IntPtr _smallImageList;

        static NativeImageList()
        {
            _directoryIconIndex = FileIconIndex("a", true);
            SHFILEINFO fileInfo = new SHFILEINFO();

            _largeImageList = SHGetFileInfo("", 0, ref fileInfo, Marshal.SizeOf(fileInfo),
                SHGFI_SHELLICONSIZE | SHGFI_SYSICONINDEX | SHGFI_LARGEICON);

            _smallImageList = SHGetFileInfo("", 0, ref fileInfo, Marshal.SizeOf(fileInfo),
                SHGFI_SHELLICONSIZE | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
        }


        #region DLLImport
        [DllImport("shell32.dll")]
        private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

        [DllImport("Shell32.dll")]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbfileInfo, uint uFlags);

        [DllImport("User32.DLL")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        #endregion DLLImport

        public static void SetIconIndex(IntPtr controlHandle)
        {
            SendMessage(controlHandle, LVM_SETIMAGELIST, (IntPtr)LVSIL_NORMAL, _largeImageList);
            SendMessage(controlHandle, LVM_SETIMAGELIST, (IntPtr)LVSIL_SMALL, _smallImageList);
        }

        public static int FileIconIndex(string fileName)
        {
            return FileIconIndex(fileName, false);
        }

        public static int DirectoryIconIndex
        {
            get { return _directoryIconIndex; }
        }

        private static int FileIconIndex(string fileName, bool isDirectory)
        {
            SHFILEINFO fileInfo = new SHFILEINFO();
            SHGetFileInfo(fileName, isDirectory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL,
                ref fileInfo,
                Marshal.SizeOf(fileInfo), SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
            return fileInfo.iIcon;
        }
    }
}
